<

ウィジェットの紹介

Flutter ウィジェットは、最新のフレームワークを使用して構築されています。 からのインスピレーション反応する。中心的な考え方は、あなたが構築するということです。 UI がウィジェットから外されます。ウィジェットは自分のビューを説明します 現在の構成と状態を考慮すると、次のようになります。 ウィジェットの状態が変化すると、ウィジェットはその説明を再構築します。 フレームワークが前の説明と順番に異なっている 基礎となるレンダリングに必要な最小限の変更を決定するため ある状態から次の状態に遷移するツリー。

こんにちは世界

最小限の Flutter アプリは単にrunApp()ウィジェットを使用した関数:

import 'package:flutter/material.dart';

void main() {
  runApp(
    const Center(
      child: Text(
        'Hello, world!',
        textDirection: TextDirection.ltr,
      ),
    ),
  );
}

runApp()関数は指定されたものを受け取りますWidgetそしてそれをウィジェットツリーのルートにします。 この例では、ウィジェット ツリーは 2 つのウィジェットで構成されます。 のCenterウィジェットとその子、Textウィジェット。 フレームワークはルート ウィジェットに画面を強制的にカバーします。 つまり、「Hello, world」というテキストが画面の中央に配置されます。 この例では、テキストの方向を指定する必要があります。 いつ70b2b83c-3579-4a5b-bbad-33bbfee45bc1ウィジェットが使用されており、 後で説明するように、これは自動的に処理されます。

アプリを作成するときは、通常、次のような新しいウィジェットを作成します。 いずれかのサブクラスですStatelessWidgetまたStatefulWidget、 ウィジェットが状態を管理するかどうかに応じて異なります。 ウィジェットの主な仕事は、build()関数、 これは、他の下位レベルのウィジェットに関してウィジェットを説明します。 フレームワークは、プロセスが完了するまで、これらのウィジェットを順番に構築します。 根底にあるものを表すウィジェットで底を突きますRenderObject、 ウィジェットのジオメトリを計算して記述します。

基本的なウィジェット

Flutter には強力な基本ウィジェットのスイートが付属しています。 そのうち、以下が一般的に使用されます。

Text
Textウィジェットを使用すると、スタイル付きのテキストを作成できます アプリケーション内で。
RowColumn
これらのフレックス ウィジェットを使用すると、柔軟なレイアウトを作成できます。 両方の水平(Row) と垂直 (Column) 方向。 これらのオブジェクトのデザインは Web に基づいています。 フレックスボックスレイアウトモデル。
Stack
直線的(水平方向または垂直方向)に配置するのではなく、 あるStackウィジェットを使用すると、ペイント順にウィジェットを互いに重ねて配置できます。 その後、Positionedの子のウィジェットStack上、右、下を基準にして配置するには、 またはスタックの左端。スタックはウェブに基づいています 絶対位置決めレイアウトモデル。
Container
Containerウィジェットを使用すると、長方形の視覚要素を作成できます。 コンテナを装飾することができます。BoxDecoration、 など 背景、境界線、または影。あContainerマージンを持たせることもできますが、 パディング、およびそのサイズに適用される制約。さらに、Container行列を使用して 3 次元空間に変換できます。

以下は、これらのウィジェットと他のウィジェットを組み合わせたいくつかの単純なウィジェットです。

import 'package:flutter/material.dart';

class MyAppBar extends StatelessWidget {
  const MyAppBar({required this.title, super.key});

  // Fields in a Widget subclass are always marked "final".

  final Widget title;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 56, // in logical pixels
      padding: const EdgeInsets.symmetric(horizontal: 8),
      decoration: BoxDecoration(color: Colors.blue[500]),
      // Row is a horizontal, linear layout.
      child: Row(
        children: [
          const IconButton(
            icon: Icon(Icons.menu),
            tooltip: 'Navigation menu',
            onPressed: null, // null disables the button
          ),
          // Expanded expands its child
          // to fill the available space.
          Expanded(
            child: title,
          ),
          const IconButton(
            icon: Icon(Icons.search),
            tooltip: 'Search',
            onPressed: null,
          ),
        ],
      ),
    );
  }
}

class MyScaffold extends StatelessWidget {
  const MyScaffold({super.key});

  @override
  Widget build(BuildContext context) {
    // Material is a conceptual piece
    // of paper on which the UI appears.
    return Material(
      // Column is a vertical, linear layout.
      child: Column(
        children: [
          MyAppBar(
            title: Text(
              'Example title',
              style: Theme.of(context) //
                  .primaryTextTheme
                  .titleLarge,
            ),
          ),
          const Expanded(
            child: Center(
              child: Text('Hello, world!'),
            ),
          ),
        ],
      ),
    );
  }
}

void main() {
  runApp(
    const MaterialApp(
      title: 'My app', // used by the OS task switcher
      home: SafeArea(
        child: MyScaffold(),
      ),
    ),
  );
}

必ず持っておいてくださいuses-material-design: trueのエントリflutterあなたのセクションpubspec.yamlファイル。事前定義されたものを使用できるようにします のセットマテリアルアイコン。通常、この行を含めることをお勧めします マテリアル ライブラリを使用している場合。

name: my_app
flutter:
  uses-material-design: true

多くのマテリアル デザイン ウィジェットは、MaterialAppテーマデータを引き継ぐために、正しく表示するために。 したがって、次のコマンドを使用してアプリケーションを実行します。MaterialApp

MyAppBarウィジェットが作成するContainer身長56 8 ピクセルの内部パディングを持つデバイスに依存しないピクセル、 左にも右にも。コンテナの中には、MyAppBarを使用しますRow子を整理するためのレイアウト。 真ん中の子は、titleウィジェットは次のようにマークされていますExpanded、 つまり、残りの利用可能なスペースを埋めるために拡張されます。 他の子供たちが食べていないもの。 複数持つことができますExpanded子供たちと決定します 使用可能なスペースを消費する比率flexに対する議論Expanded

MyScaffoldウィジェットはその子を垂直列に編成します。 列の先頭に次のインスタンスを配置します。MyAppBar、 アプリバーを通過するTextタイトルとして使用するウィジェット。 ウィジェットを引数として他のウィジェットに渡すことは強力なテクニックです これにより、幅広い用途で再利用できる汎用ウィジェットを作成できます。 さまざまな方法。ついに、MyScaffoldを使用しますExpanded残りのスペースを本体で埋めるため、 中央に配置されたメッセージで構成されます。

詳細については、を参照してください。レイアウト

マテリアルコンポーネントの使用

Flutter は、アプリの構築に役立つ多数のウィジェットを提供します マテリアルデザインを踏襲したもの。マテリアル アプリは次から始まります。MaterialAppウィジェット: 多数の便利なウィジェットを構築します アプリのルートに、Navigator、 文字列で識別されるウィジェットのスタックを管理します。 「ルート」とも呼ばれます。のNavigatorスムーズに移行できます アプリケーションの画面間。の使用MaterialAppウィジェットは完全にオプションですが、良い習慣となります。

import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      title: 'Flutter Tutorial',
      home: TutorialHome(),
    ),
  );
}

class TutorialHome extends StatelessWidget {
  const TutorialHome({super.key});

  @override
  Widget build(BuildContext context) {
    // Scaffold is a layout for
    // the major Material Components.
    return Scaffold(
      appBar: AppBar(
        leading: const IconButton(
          icon: Icon(Icons.menu),
          tooltip: 'Navigation menu',
          onPressed: null,
        ),
        title: const Text('Example title'),
        actions: const [
          IconButton(
            icon: Icon(Icons.search),
            tooltip: 'Search',
            onPressed: null,
          ),
        ],
      ),
      // body is the majority of the screen.
      body: const Center(
        child: Text('Hello, world!'),
      ),
      floatingActionButton: const FloatingActionButton(
        tooltip: 'Add', // used by assistive technologies
        onPressed: null,
        child: Icon(Icons.add),
      ),
    );
  }
}

コードが から切り替わったので、MyAppBarMyScaffoldAppBarScaffoldウィジェット、およびからmaterial.dart、 アプリはもう少しマテリアルっぽくなり始めています。 たとえば、アプリ バーには影があり、タイトル テキストは影を継承します。 スタイルを自動的に修正します。フローティングアクションボタンも追加されています。

ウィジェットは引数として他のウィジェットに渡されることに注意してください。 のScaffoldwidget はさまざまなウィジェットを次のように受け取ります。 名前付き引数。それぞれはScaffold適切な場所にレイアウトします。同様に、AppBarウィジェットを使用すると、leadingウィジェット、およびactionstitleウィジェット。 このパターンはフレームワーク全体で繰り返し発生するものであり、 独自のウィジェットを設計するときに検討してください。

詳細については、を参照してください。マテリアルコンポーネントウィジェット

ジェスチャーの処理

ほとんどのアプリケーションには、システムとの何らかの形式のユーザー対話が含まれています。 インタラクティブなアプリケーションを構築する最初のステップは、 入力ジェスチャー。簡単なボタンを作成して、その仕組みを確認してください。

import 'package:flutter/material.dart';

class MyButton extends StatelessWidget {
  const MyButton({super.key});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        print('MyButton was tapped!');
      },
      child: Container(
        height: 50,
        padding: const EdgeInsets.all(8),
        margin: const EdgeInsets.symmetric(horizontal: 8),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(5),
          color: Colors.lightGreen[500],
        ),
        child: const Center(
          child: Text('Engage'),
        ),
      ),
    );
  }
}

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyButton(),
        ),
      ),
    ),
  );
}

GestureDetectorウィジェットにはビジュアルがありません 表現ではなく、その代わりに、 ユーザー。ユーザーがタップすると、Container、 のGestureDetectorそれを呼び出しますonTap()コールバック、この中で コンソールにメッセージを出力する場合。使用できますGestureDetectorさまざまな入力ジェスチャを検出するため、 タップ、ドラッグ、スケールなど。

多くのウィジェットはGestureDetector提供する 他のウィジェットのオプションのコールバック。たとえば、IconButtonElevatedButton、 とFloatingActionButtonウィジェットにはonPressed()ユーザーがウィジェットをタップしたときにトリガーされるコールバック。

詳細については、を参照してください。Flutter のジェスチャー

入力に応じてウィジェットを変更する

これまでのところ、このページではステートレス ウィジェットのみが使用されています。 ステートレス ウィジェットは親ウィジェットから引数を受け取ります。 彼らが保管しているものfinalメンバー変数。 ウィジェットが要求されたときbuild()、保存されているこれらを使用します 値を使用して、作成するウィジェットの新しい引数を取得します。

より複雑なエクスペリエンスを構築するには、たとえば、 ユーザー入力に対してより興味深い方法で反応する - アプリケーション 通常、何らかの状態を保持します。 flutterの使用法StatefulWidgetsキャプチャします このアイデア。StatefulWidgets生成方法を知っている特別なウィジェットですStateオブジェクトは状態を保持するために使用されます。 以下を使用して、この基本的な例を考えてみましょう。ElevatedButton先に述べた:

import 'package:flutter/material.dart';

class Counter extends StatefulWidget {
  // This class is the configuration for the state.
  // It holds the values (in this case nothing) provided
  // by the parent and used by the build  method of the
  // State. Fields in a Widget subclass are always marked
  // "final".

  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      // This call to setState tells the Flutter framework
      // that something has changed in this State, which
      // causes it to rerun the build method below so that
      // the display can reflect the updated values. If you
      // change _counter without calling setState(), then
      // the build method won't be called again, and so
      // nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called,
    // for instance, as done by the _increment method above.
    // The Flutter framework has been optimized to make
    // rerunning build methods fast, so that you can just
    // rebuild anything that needs updating rather than
    // having to individually changes instances of widgets.
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Increment'),
        ),
        const SizedBox(width: 16),
        Text('Count: $_counter'),
      ],
    );
  }
}

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Counter(),
        ),
      ),
    ),
  );
}

なぜだろうと不思議に思うかもしれませんStatefulWidgetStateは別個のオブジェクトです。 Flutter では、これら 2 種類のオブジェクトのライフサイクルは異なります。Widgetsプレゼンテーションを構築するために使用される一時的なオブジェクトです。 アプリケーションの現在の状態。State一方ではオブジェクト hand、呼び出し間で永続的build()、情報を記憶できるようになります。

上の例ではユーザー入力を受け入れ、直接使用します。 その結果build()方法。より複雑なアプリケーションでは、 ウィジェット階層のさまざまな部分が さまざまな懸念事項に責任を負います。たとえば、1 つ ウィジェットは複雑なユーザー インターフェイスを表示する場合があります 具体的な情報を収集することを目的として、 日付や場所など、別のウィジェットが その情報を使用して全体的なプレゼンテーションを変更します。

Flutter では、変更通知はウィジェットの「上」に流れます 現在の状態が流れる間、コールバックを介して階層を移動します。 プレゼンテーションを行うステートレスなウィジェットまで「ダウン」します。 このフローをリダイレクトする共通の親は、State。 次の少し複雑な例は、その方法を示しています。 これは実際には機能します:

import 'package:flutter/material.dart';

class CounterDisplay extends StatelessWidget {
  const CounterDisplay({required this.count, super.key});

  final int count;

  @override
  Widget build(BuildContext context) {
    return Text('Count: $count');
  }
}

class CounterIncrementor extends StatelessWidget {
  const CounterIncrementor({required this.onPressed, super.key});

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: const Text('Increment'),
    );
  }
}

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      ++_counter;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        CounterIncrementor(onPressed: _increment),
        const SizedBox(width: 16),
        CounterDisplay(count: _counter),
      ],
    );
  }
}

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Counter(),
        ),
      ),
    ),
  );
}

2 つの新しいステートレス ウィジェットが作成されていることに注目してください。 ~の懸念事項を明確に分離する表示するカウンタ (CounterDisplay) と変化カウンタ (CounterIncrementor)。 最終的な結果は前の例と同じですが、 責任の分離により、より複雑な作業が可能になります。 個々のウィジェットにカプセル化されます。 親の単純さを維持しながら。

詳細については、以下を参照してください。

  • StatefulWidget
  • setState()

すべてをひとつにまとめる

以下は、以下をまとめたより完全な例です。 これらの概念: 架空のショッピング アプリケーションでは、さまざまな情報が表示されます。 販売されている製品を管理し、ショッピング カートを管理します。 購入予定のもの。まずプレゼンテーション クラスを定義します。ShoppingListItem:

import 'package:flutter/material.dart';

class Product {
  const Product({required this.name});

  final String name;
}

typedef CartChangedCallback = Function(Product product, bool inCart);

class ShoppingListItem extends StatelessWidget {
  ShoppingListItem({
    required this.product,
    required this.inCart,
    required this.onCartChanged,
  }) : super(key: ObjectKey(product));

  final Product product;
  final bool inCart;
  final CartChangedCallback onCartChanged;

  Color _getColor(BuildContext context) {
    // The theme depends on the BuildContext because different
    // parts of the tree can have different themes.
    // The BuildContext indicates where the build is
    // taking place and therefore which theme to use.

    return inCart //
        ? Colors.black54
        : Theme.of(context).primaryColor;
  }

  TextStyle? _getTextStyle(BuildContext context) {
    if (!inCart) return null;

    return const TextStyle(
      color: Colors.black54,
      decoration: TextDecoration.lineThrough,
    );
  }

  @override
  Widget build(BuildContext context) {
    return ListTile(
      onTap: () {
        onCartChanged(product, inCart);
      },
      leading: CircleAvatar(
        backgroundColor: _getColor(context),
        child: Text(product.name[0]),
      ),
      title: Text(product.name, style: _getTextStyle(context)),
    );
  }
}

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: Center(
          child: ShoppingListItem(
            product: const Product(name: 'Chips'),
            inCart: true,
            onCartChanged: (product, inCart) {},
          ),
        ),
      ),
    ),
  );
}

ShoppingListItemウィジェットは一般的なパターンに従います ステートレスなウィジェットの場合。受け取った値を保存します そのコンストラクター内でfinalメンバー変数、 それはその後、その実行中に使用されますbuild()関数。 たとえば、inCartブール値は 2 つのビジュアルを切り替えます 外観: 現在の原色を使用するもの テーマとグレーを使用するテーマです。

ユーザーがリスト項目をタップしても、ウィジェットは変更されません これはinCart直接的に価値を感じます。代わりに、ウィジェットはonCartChanged親ウィジェットから受け取った関数。 このパターンを使用すると、ウィジェットの上位に状態を保存できます これにより、状態が長期間持続します。 極端な場合、ウィジェットに保存されている状態は次のように渡されます。runApp()の生涯にわたって持続します 応用。

保護者が受け取ると、onCartChanged折り返し電話、 親は内部状態を更新し、それがトリガーになります 新しいインスタンスを再構築して作成する親 のShoppingListItem新しいものと一緒にinCart価値。 親は新しいインスタンスを作成しますが、ShoppingListItem再構築する場合、その操作は安価です フレームワークは新しく構築されたウィジェットを以前のウィジェットと比較するため、 構築されたウィジェットを作成し、相違点のみを基になるウィジェットに適用します。RenderObject

変更可能な状態を保存する親ウィジェットの例を次に示します。

import 'package:flutter/material.dart';

class Product {
  const Product({required this.name});

  final String name;
}

typedef CartChangedCallback = Function(Product product, bool inCart);

class ShoppingListItem extends StatelessWidget {
  ShoppingListItem({
    required this.product,
    required this.inCart,
    required this.onCartChanged,
  }) : super(key: ObjectKey(product));

  final Product product;
  final bool inCart;
  final CartChangedCallback onCartChanged;

  Color _getColor(BuildContext context) {
    // The theme depends on the BuildContext because different
    // parts of the tree can have different themes.
    // The BuildContext indicates where the build is
    // taking place and therefore which theme to use.

    return inCart //
        ? Colors.black54
        : Theme.of(context).primaryColor;
  }

  TextStyle? _getTextStyle(BuildContext context) {
    if (!inCart) return null;

    return const TextStyle(
      color: Colors.black54,
      decoration: TextDecoration.lineThrough,
    );
  }

  @override
  Widget build(BuildContext context) {
    return ListTile(
      onTap: () {
        onCartChanged(product, inCart);
      },
      leading: CircleAvatar(
        backgroundColor: _getColor(context),
        child: Text(product.name[0]),
      ),
      title: Text(
        product.name,
        style: _getTextStyle(context),
      ),
    );
  }
}

class ShoppingList extends StatefulWidget {
  const ShoppingList({required this.products, super.key});

  final List<Product> products;

  // The framework calls createState the first time
  // a widget appears at a given location in the tree.
  // If the parent rebuilds and uses the same type of
  // widget (with the same key), the framework re-uses
  // the State object instead of creating a new State object.

  @override
  State<ShoppingList> createState() => _ShoppingListState();
}

class _ShoppingListState extends State<ShoppingList> {
  final _shoppingCart = <Product>{};

  void _handleCartChanged(Product product, bool inCart) {
    setState(() {
      // When a user changes what's in the cart, you need
      // to change _shoppingCart inside a setState call to
      // trigger a rebuild.
      // The framework then calls build, below,
      // which updates the visual appearance of the app.

      if (!inCart) {
        _shoppingCart.add(product);
      } else {
        _shoppingCart.remove(product);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Shopping List'),
      ),
      body: ListView(
        padding: const EdgeInsets.symmetric(vertical: 8),
        children: widget.products.map((product) {
          return ShoppingListItem(
            product: product,
            inCart: _shoppingCart.contains(product),
            onCartChanged: _handleCartChanged,
          );
        }).toList(),
      ),
    );
  }
}

void main() {
  runApp(const MaterialApp(
    title: 'Shopping App',
    home: ShoppingList(
      products: [
        Product(name: 'Eggs'),
        Product(name: 'Flour'),
        Product(name: 'Chocolate chips'),
      ],
    ),
  ));
}

ShoppingListクラスが拡張するStatefulWidget、 つまり、このウィジェットは変更可能な状態を保存します。 ときShoppingListウィジェットが最初に挿入されます ツリーに挿入すると、フレームワークはcreateState()関数 新しいインスタンスを作成するには_ShoppingListStateそれと関連付けるために ツリー内の場所。 (サブクラスがState通常、先頭にアンダースコアを付けて名前が付けられます プライベート実装の詳細であることを示します)。 このウィジェットの親が再構築されると、親は新しいインスタンスを作成します のShoppingListですが、フレームワークは_ShoppingListState呼び出すのではなく、すでにツリー内にあるインスタンスcreateStateまた。

現在のプロパティにアクセスするにはShoppingList、 の_ShoppingListStateそれを使用できますwidget財産。 親が再構築して新しいものを作成する場合、ShoppingList、 の_ShoppingListState新しいウィジェット値で再構築します。 通知を希望する場合は、widgetプロパティの変更、 をオーバーライドするdidUpdateWidget()渡される関数 のoldWidget古いウィジェットと比較できるようにする 現在のウィジェット。

取り扱う際には、onCartChangedコールバック、_ShoppingListStateに製品を追加または削除することで内部状態を変更します。_shoppingCart。内部が変更されたことをフレームワークに通知するため 状態では、それらの呼び出しをsetState()電話。 電話をかけるsetStateこのウィジェットをダーティとしてマークし、再構築するようにスケジュールします 次回アプリが画面を更新する必要があるとき。 電話を忘れた場合setState内部を変更する場合 ウィジェットの状態を考慮すると、フレームワークはウィジェットがどのような状態であるかを認識できません。 ダーティでウィジェットを呼び出せない可能性がありますbuild()関数、 つまり、ユーザー インターフェイスが更新されて反映されない可能性があります。 変わった状態。このように状態を管理することで、 作成と実行のために別のコードを記述する必要はありません。 子ウィジェットを更新しています。代わりに、単に実装するだけですbuild両方の状況を処理する関数。

ウィジェットのライフサイクル イベントへの応答

電話した後createState()StatefulWidget、 フレームワークは新しい状態オブジェクトをツリーに挿入し、 それから電話しますinitState()状態オブジェクトについて。 のサブクラスState上書きできるinitState仕事をする それは一度だけ起こる必要があります。たとえば、オーバーライドしますinitStateアニメーションを設定したり、プラットフォーム サービスを購読したりできます。 の実装initState開始するために必要です 電話することでsuper.initState

状態オブジェクトが不要になった場合、 フレームワークが呼び出すdispose()状態オブジェクトについて。 オーバーライドdisposeクリーンアップ作業を行う関数。 たとえば、オーバーライドしますdisposeタイマーをキャンセルするか、 プラットフォームサービスの購読を解除します。の実装dispose通常は電話で終わりますsuper.dispose

詳細については、を参照してください。State

キー

キーを使用してフレームワークが一致するウィジェットを制御します ウィジェットが再構築されるときに他のウィジェットと連携します。デフォルトでは、 フレームワークは現在および以前のビルドのウィジェットと一致します 彼らによるとruntimeTypeそしてそれらが現れる順番。 キーを使用する場合、フレームワークでは 2 つのウィジェットが 同じkey同じようにruntimeType

キーは、多数のインスタンスを構築するウィジェットで最も役立ちます。 同じ種類のウィジェット。たとえば、ShoppingListウィジェット、 これで十分に構築できますShoppingListItemインスタンスから 表示領域を塗りつぶします。

  • キーがない場合、現在のビルドの最初のエントリ 常に前のビルドの最初のエントリと同期します。 たとえ意味的には、リストの最初のエントリが 画面外にスクロールされ、ビューポートに表示されなくなります。

  • リスト内の各エントリに「セマンティック」キーを割り当てることで、 無限リストはより効率的になる可能性があります。 フレームワークは、一致するセマンティック キーを使用してエントリを同期します したがって、同様の(または同一の)視覚的外観になります。 さらに、エントリを意味的に同期するとは、 ステートフルな子ウィジェットに保持される状態はアタッチされたままになります のエントリではなく、同じセマンティック エントリに ビューポート内の同じ数値位置。

詳細については、「KeyAPI。

グローバルキー

グローバル キーを使用して子ウィジェットを一意に識別します。 グローバルキーは全体にわたってグローバルに一意である必要があります 必要なローカルキーとは異なり、ウィジェット階層 兄弟間でのみ一意である必要があります。なぜなら彼らはそうであるから グローバルに一意であるため、グローバル キーを使用して ウィジェットに関連付けられた状態を取得します。

詳細については、「GlobalKeyAPI。